package com.zhitan.netty.decoder;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.NumberUtil;
import com.zhitan.netty.bean.NettyMessage;
import com.zhitan.netty.util.CRC16Modbus;
import com.zhitan.netty.util.Cp56Time2aUtil;
import com.zhitan.netty.util.HexHandleUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.zhitan.netty.constant.DataMapKeyConstant.*;
import static com.zhitan.netty.constant.FrameTypeConstant.*;
import static com.zhitan.netty.constant.NettyConstant.*;

/**
 * 解码数据
 **/
@Slf4j
public class ChargingYunDecoder extends ByteToMessageDecoder {
    /**
     * 消息体前数据长度
     */
    private static final int MSG_BODY_PRE_LENGTH = 4;
    /**
     * MAP初始化容量
     */
    private static final int MAP_INITIAL_CAPACITY = 16;
    /**
     * 最大数据长度
     */
    private static final short MAX_DATA_LENGTH = 1024;
    /**
     * CRC校验开始帧
     */
    private static final int CRC_CHECK_BEGIN_INDEX = 2;
    private static final double NUM_FORMAT_0_1 = 0.1;
    private static final double NUM_FORMAT_0_01 = 0.01;
    private static final double NUM_FORMAT_0_001 = 0.001;
    private static final double NUM_FORMAT_0_0001 = 0.0001;
    private static final double NUM_FORMAT_0_00001 = 0.00001;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
        NettyMessage nettyMessage = new NettyMessage();
        // 读取原始数据串 不会改变ByteBuf 索引
        String originalData = ByteBufUtil.hexDump(buffer);
        log.info("当前原始数据:{}", originalData);
        nettyMessage.setOriginalData(originalData);
        // 获取数据总长度
        int allLength = buffer.readableBytes();
        nettyMessage.setAllDataLength(allLength);
        buffer.markReaderIndex();
        // 起始标识
        byte startFlag = buffer.readByte();
        while (START_FLAG != startFlag) {
            return;
        }
        nettyMessage.setStartFlag(startFlag);
        // 数据域字节数 序列号域+加密标志+帧类型标志+消息体
        // 起始标志	数据长度	序列号域	加密标志	帧类型标志	消息体	帧校验域
        // 1 字节	1 字节	2 字节	1 字节	1 字节	    N 字节	2 字节
        short dataLength = buffer.readUnsignedByte();
        if (dataLength > MAX_DATA_LENGTH) {
            buffer.markReaderIndex(); //mark ReaderIndex
            return;
        }
        nettyMessage.setDataLength(dataLength);
        // 序列号域
        short packageIndex = buffer.readShortLE();
        nettyMessage.setPackageIndex(packageIndex);
        // 加密标志只针对消息体（数据单元）
        nettyMessage.setEncryptionType(buffer.readByte());
        // 帧类型标志
        byte frameType = buffer.readByte();
        nettyMessage.setFrameType(frameType);
        log.info("帧类型标志:{}, 数据长度:{}，数据总长度:{},源数据:{}",
                HexUtil.toHex(frameType), dataLength, allLength, originalData);
        // 消息体需要扣除的前缀数据 （序列号域2+加密标志1+帧类型标志1）
        if (allLength < (dataLength + MSG_BODY_PRE_LENGTH)) {
            // 重置 ByteBuf 读指针位置
            buffer.resetReaderIndex();
            log.info("读取长度不够,返回等待:{}", originalData);
            return;
        }
        // 消息体信息
        ByteBuf msgBody = buffer.readBytes(dataLength - MSG_BODY_PRE_LENGTH);
        // 消息体解析后的数据
        Map<String, Object> dataMap = new HashMap<>(MAP_INITIAL_CAPACITY);
        // 放入原始书
        dataMap.put(KEY_ORIGINAL_DATA, nettyMessage.getOriginalData());
        // 帧校验域
        short frameCheck = buffer.readShortLE();
        // CRC校验的数据
        byte[] allData = HexUtil.decodeHex(originalData);
        byte[] checkData = HexHandleUtil.subBytes(allData, CRC_CHECK_BEGIN_INDEX, dataLength);
        // CRC校验的结果
        short check = CRC16Modbus.get(checkData);
        log.info("CRC校验 帧校验域：{} 校验结果：{}", String.valueOf(frameCheck), String.valueOf(check));
        nettyMessage.setFrameCheck(frameCheck);
        // 处理消息体消息
        switch (nettyMessage.getFrameType()) {
            case FRAME_TYPE_DEVICE_LOGIN:
                // 0x01 充电桩登录认证
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_TYPE, ByteBufUtil.hexDump(msgBody.readBytes(1)));
                dataMap.put(KEY_GUN_NUM, ByteBufUtil.hexDump(msgBody.readBytes(1)));
                dataMap.put(KEY_EDITION, ByteBufUtil.hexDump(msgBody.readBytes(1)));
                dataMap.put(KEY_SOFT_EDITION, ByteBufUtil.hexDump(msgBody.readBytes(8)));
                dataMap.put(KEY_NIC_TYPE, ByteBufUtil.hexDump(msgBody.readBytes(1)));
                dataMap.put(KEY_SIM_INFO, ByteBufUtil.hexDump(msgBody.readBytes(10)));
                dataMap.put(KEY_SERVICE, ByteBufUtil.hexDump(msgBody.readBytes(1)));
                break;
            case FRAME_TYPE_HEART_BEAT:
                // 0x03 充电桩心跳包
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put(KEY_GUN_STATUS, msgBody.readByte());
                break;
            case FRAME_TYPE_FEE_MODEL_CHECK_REQ:
                // 0x05 计费模型验证请求
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                // 计费模型编号,首次连接到平台时置零
                dataMap.put(KEY_FEE_MODEL_NO, msgBody.readShortLE());
                break;
            case FRAME_TYPE_FEE_MODEL_REQ:
                // 0x09 充电桩计费模型请求
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                break;
            case FRAME_TYPE_OFFLINE_DATA:
                // 0x13 离线监测数据（上传实时监测数据）
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                // 状态 0x00：离线 0x01：故障 0x02：空闲 0x03：充电 0x04:充电桩就绪  需做到变位上送
                dataMap.put(KEY_GUN_STATUS, msgBody.readByte());
                // 枪是否归位  0x00 否 0x01 是 0x02 未知
                dataMap.put(KEY_GUN_HOMING, msgBody.readByte());
                // 是否插枪 0x00 否 0x01 是
                dataMap.put(KEY_GUN_INSERT, msgBody.readByte());
                // 输出电压 精确到小数点后一位；待机置零
                dataMap.put(KEY_OUT_VOLTAGE, NumberUtil.mul(msgBody.readShortLE(), NUM_FORMAT_0_1));
                // 输出电流
                dataMap.put(KEY_OUT_CURRENT, NumberUtil.mul(msgBody.readShortLE(), NUM_FORMAT_0_1));
                // 枪线温度  一个字节
                dataMap.put(KEY_TEMPERATURE, msgBody.readByte() - 50);
                // 枪线编码
                msgBody.skipBytes(8);
                // BMS 当前荷电状态 SOC（ %） 1%/位，0%偏移量；数据范围：0~100%
                dataMap.put(KEY_SOC, msgBody.readByte());
                dataMap.put(KEY_MAX_TEMPERATURE, msgBody.readByte() - 50);
                // 累计充电时间 1 min/位，0 min 偏移量；数据范围：0~600 min
                dataMap.put(KEY_OUT_TIME, msgBody.readShortLE());
                // 剩余时间 单位：min；待机置零、交流桩置零
                dataMap.put(KEY_SUR_TIME, msgBody.readShortLE());
                // 充电度数 精确到小数点后四位；待机置零
                dataMap.put(KEY_HAS_CHARGE_POWER, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                // 计损充电度数
                msgBody.skipBytes(4);
                // 已充金额
                dataMap.put(KEY_TOTAL_FEE, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                dataMap.put(KEY_CODE_ERROR, msgBody.readShortLE());
                break;
            case FRAME_TYPE_CHARGING_HANDSHAKE:
                // 0x15
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put("bmsVersion", msgBody.readBytes(3));
                dataMap.put("batteryType", msgBody.readByte());
                dataMap.put("batteryCapacity", msgBody.readShortLE());
                dataMap.put("batteryVoltage", msgBody.readShortLE());
                dataMap.put("batteryManufacture", msgBody.readInt());
                dataMap.put("batterySerialNo", msgBody.readInt());
                dataMap.put("batteryYear", msgBody.readByte());
                dataMap.put("batteryMonth", msgBody.readByte());
                dataMap.put("batteryDay", msgBody.readByte());
                dataMap.put("useTime", msgBody.readBytes(3));
                dataMap.put("right", msgBody.readByte());
                dataMap.put("mark", msgBody.readByte());
                dataMap.put("carNo", msgBody.readBytes(17));
                dataMap.put("version", msgBody.readBytes(8));
                break;
            case FRAME_TYPE_BMS_OUTPUT_DATA:
                // 0x23 参数配置
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                //  BMS 电压需求 2  BMS 电流需求  2
                msgBody.skipBytes(4);
                // BMS 充电模式 0x01：恒压充电；0x02：恒流充电
                dataMap.put(KEY_CHARGE_MODE, msgBody.readByte());
                // BMS 充电电压测量值 NUM_FORMAT_0_1 V/位，0 V 偏移量
                dataMap.put(KEY_TEST_VOLTAGE, NumberUtil.mul(msgBody.readShortLE(), NUM_FORMAT_0_1));
                // BMS 充电电流测量值 NUM_FORMAT_0_1 A/位，-400 A 偏移量
                dataMap.put(KEY_TEST_CURRENT, NumberUtil.mul(msgBody.readShortLE(), NUM_FORMAT_0_1));
                // BMS 最高单体动力蓄电池电压及组号
                msgBody.skipBytes(2);
                // BMS 当前荷电状态 SOC（ %） 1%/位，0%偏移量；数据范围：0~100%
                dataMap.put(KEY_SOC, msgBody.readByte());
                // BMS 估算剩余充电时间 1 min/位，0 min 偏移量；数据范围：0~600 min
                dataMap.put(KEY_SUR_TIME, msgBody.readShortLE());
                // 电桩电压输出值 NUM_FORMAT_0_1 V/位，0 V 偏移量
                dataMap.put(KEY_OUT_VOLTAGE, NumberUtil.mul(msgBody.readShortLE(), NUM_FORMAT_0_1));
                // 电桩电流输出值 NUM_FORMAT_0_1 A/位，-400 A 偏移量
                dataMap.put(KEY_OUT_CURRENT, NumberUtil.mul(msgBody.readShortLE(), NUM_FORMAT_0_1));
                // 累计充电时间 1 min/位，0 min 偏移量；数据范围：0~600 min
                dataMap.put(KEY_OUT_TIME, msgBody.readShortLE());
                break;
            case FRAME_TYPE_PILE_START_CHARGING:
                // 0x31 充电桩主动申请启动充电   即卡充电请求
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                // 启动方式  0x01 表示通过刷卡启动充电  0x02 表求通过账号启动充电（硬件暂不支持） 0x03 表示vin码启动充电
                dataMap.put(KEY_START_METHOD, msgBody.readByte());
                // 是否需要密码 0x00 不需要 0x01 需要
                dataMap.put(KEY_NEED_PASSWORD, msgBody.readByte());
                // 账号或者物理卡号 不足 8 位补 0，具体见示例
                dataMap.put(KEY_PHYSICAL_CARD_NO, msgBody.readLongLE());
                // 输入密码 对用户输入的密码进行16 位MD5加密，采用小写上传  ---目前没有
                dataMap.put(KEY_PASSWORD, msgBody.readBytes(16));
                // VIN 码 启动方式为vin码启动充电时上送,其他方式置零( ASCII码)，VIN码需要反序上送  ---目前没有
                dataMap.put(KEY_VIN_CODE, msgBody.readBytes(17));
                break;
            case FRAME_TYPE_START_CHARGING_RES:
                // 0x33 远程启动命令回复  桩->平台
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                // 枪号
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                // 0x00失败 0x01成功
                dataMap.put(KEY_START_RESULT, msgBody.readByte());
                // 0x00 无 0x01 设备编号不匹配 0x02 枪已在充电 0x03 设备故障  0x04 设备离线  0x05 未插枪
                dataMap.put(KEY_ERROR_CODE, msgBody.readByte());
                break;
            case FRAME_TYPE_STOP_CHARGING_RES:
                // 0x35 远程停机命令回复
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                // 停止结果 0x00失败 0x01成功
                dataMap.put(KEY_STOP_RESULT, msgBody.readByte());
                // 失败原因 0x00:无 0x01 设备编号不匹配  0x02 枪未处于充电状态 0x03 其他
                dataMap.put(KEY_ERROR_REASON, msgBody.readByte());
                break;
            case FRAME_TYPE_TRANSACTIONS:
                //  0x3B 交易记录
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                // 枪号
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put(KEY_START_TIME, Cp56Time2aUtil.toDateString(ByteBufUtil.getBytes(msgBody.readBytes(DATETIME_LENGTH))));
                dataMap.put(KEY_END_TIME, Cp56Time2aUtil.toDateString(ByteBufUtil.getBytes(msgBody.readBytes(DATETIME_LENGTH))));
                dataMap.put(KEY_PRICE_0, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_00001));
                dataMap.put(KEY_POWER_0, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                dataMap.put(KEY_LOSE_0, msgBody.readIntLE());
                dataMap.put(KEY_AMOUNT_0, msgBody.readIntLE());
                dataMap.put(KEY_PRICE_1, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_00001));
                dataMap.put(KEY_POWER_1, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                dataMap.put(KEY_LOSE_1, msgBody.readIntLE());
                dataMap.put(KEY_AMOUNT_1, msgBody.readIntLE());
                dataMap.put(KEY_PRICE_2, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_00001));
                dataMap.put(KEY_POWER_2, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                dataMap.put(KEY_LOSE_2, msgBody.readIntLE());
                dataMap.put(KEY_AMOUNT_2, msgBody.readIntLE());
                dataMap.put(KEY_PRICE_3, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_00001));
                dataMap.put(KEY_POWER_3, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                dataMap.put(KEY_LOSE_3, msgBody.readIntLE());
                dataMap.put(KEY_AMOUNT_3, msgBody.readIntLE());
                // 电表总起值 和 电表总止值
                msgBody.skipBytes(10);
                dataMap.put(KEY_TOTAL_POWER, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                dataMap.put(KEY_LOSE_POWER, msgBody.readIntLE());
                dataMap.put(KEY_TOTAL_AMOUNT, NumberUtil.mul(msgBody.readIntLE(), NUM_FORMAT_0_0001));
                // 电动汽车唯一标识 ASCII
                msgBody.skipBytes(17);
                // 交易标识 0x01：app 启动  0x02：卡启动  0x04：离线卡启动  0x05: vin 码启动充电
                dataMap.put(KEY_TRANSACTION_FLAG, msgBody.readByte());
                dataMap.put(KEY_TRANSACTION_TIME, Cp56Time2aUtil.toDateString(ByteBufUtil.getBytes(msgBody.readBytes(DATETIME_LENGTH))));
                dataMap.put(KEY_STOP_REASON, msgBody.readByte());
                dataMap.put(KEY_CARD_NO, msgBody.readLongLE());
                break;
            case FRAME_TYPE_SYNC_CARD_RES:
                //  离线卡数据同步应答 0x43
                log.info("离线卡数据同步应答，设备编号：{},结果：{}，错误原因：{}", ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)), msgBody.readByte(), msgBody.readByte());
                break;
            case FRAME_TYPE_SET_WORK_SETTING_RES:
                // 0x51 设置工作参数结果 功率设置结果 云快充1.8版本
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                // 结果0x0：成功  0x1：装编码比对失败  0x2：不在充电中  0x3：参数错误
                dataMap.put(KEY_RESULT, msgBody.readByte());
                break;
            case FRAME_TYPE_BOOKING_TIME_RESPONSE:
                // 0x53 桩端确认平台设置桩端预约
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put(KEY_RESULT, msgBody.readByte());
                break;
            case FRAME_TYPE_TIMING_RES:
                // 对时设置应答 0x55
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_NOW, Cp56Time2aUtil.toDate(ByteBufUtil.getBytes(msgBody.readBytes(DATETIME_LENGTH))));
                break;
            case FRAME_TYPE_FEE_RES:
                // 计费模型应答 0x57
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                // 设置结果  0x00 失败 0x01 成功
                dataMap.put(KEY_RESULT, msgBody.readByte());
                break;
            case FRAME_TYPE_BMS_STOP:
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                //停止原因
                dataMap.put(KEY_STOP_REASON, msgBody.readByte());
                dataMap.put(KEY_CODE_ERROR, msgBody.readShortLE());
                dataMap.put(KEY_CODE_MSG, msgBody.readByte());
                break;
            case FRAME_TYPE_BATTERY_CHARGER_STOP_0X21:
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                //停止原因
                dataMap.put("stopReason", msgBody.readByte());
                dataMap.put("codeError", msgBody.readShortLE());
                dataMap.put("codeMsg", msgBody.readByte());
                break;
            case FRAME_TYPE_BMS_INFO_0X25:
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put("1", msgBody.readByte());
                dataMap.put("2", msgBody.readByte());
                dataMap.put("3", msgBody.readByte());
                dataMap.put("4", msgBody.readByte());
                dataMap.put("5", msgBody.readByte());
                dataMap.put("21", msgBody.readShortLE());
                break;
            case FRAME_TYPE_PARAM_SET:
                // 0x17 参数配置
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put("maxVoltageStand", msgBody.readShortLE());
                dataMap.put("maxElectric", msgBody.readShortLE());
                dataMap.put("maxPower", msgBody.readShortLE());
                dataMap.put("maxVoltage", msgBody.readShortLE());
                dataMap.put("maxTemp", msgBody.readByte());
                dataMap.put("soc", msgBody.readShortLE());
                dataMap.put("curVoltage", msgBody.readShortLE());
                dataMap.put("maxOutVoltage", msgBody.readShortLE());
                dataMap.put("minOutVoltage", msgBody.readShortLE());
                dataMap.put("maxOutElectric", msgBody.readShortLE());
                dataMap.put("minOutElectric", msgBody.readShortLE());
                break;
            case FRAME_TYPE_CHARGING_END:
                // 0x19 结束充电
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put("endSoc", msgBody.readByte());
                dataMap.put("minVoltageStand", msgBody.readShortLE());
                dataMap.put("maxVoltageStand", msgBody.readShortLE());
                dataMap.put("minTemp", msgBody.readByte());
                dataMap.put("maxTemp", msgBody.readByte());
                dataMap.put("chargeTime", msgBody.readShortLE());
                dataMap.put("outPower", msgBody.readShortLE());
                dataMap.put("machineNo", msgBody.readInt());
                break;
            case FRAME_TYPE_ERROR_DATA:
                // 0X1B 错误报文
                dataMap.put(KEY_TRANSACTION_NO, ByteBufUtil.hexDump(msgBody.readBytes(TRANSACTION_NO_LENGTH)));
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put("timeout00", msgBody.readShortLE());
                dataMap.put("timoutaa", msgBody.readShortLE());
                dataMap.put("mark", msgBody.readIntLE());
                dataMap.put("timeoutNtp", msgBody.readShortLE());
                dataMap.put("timeoutReadyCharge", msgBody.readShortLE());
                dataMap.put("mark1", msgBody.readIntLE());
                dataMap.put("timoutStatus", msgBody.readShortLE());
                dataMap.put("timeoutEndStatus", msgBody.readShortLE());
                dataMap.put("mark2", msgBody.readIntLE());
                dataMap.put("timeoutChargerStatistic", msgBody.readShortLE());
                dataMap.put("bmsOther", ByteBufUtil.hexDump(msgBody.readBytes(6)));
                dataMap.put("timeoutCarRecognize", msgBody.readShortLE());
                dataMap.put("bmsFinishReady", msgBody.readShortLE());
                dataMap.put("mark3", msgBody.readIntLE());
                dataMap.put("timeoutAllStatus", msgBody.readShortLE());
                dataMap.put("timeoutAskCharge", msgBody.readShortLE());
                dataMap.put("timeoutEndCharge", msgBody.readShortLE());
                dataMap.put("mark4", msgBody.readShortLE());
                dataMap.put("timeoutBmsStatistics", msgBody.readShortLE());
                dataMap.put("other", ByteBufUtil.hexDump(msgBody.readBytes(6)));
                break;
            case FRAME_TYPE_CHANGE_AMOUNT_RESPONSE:
                // 0x41 更新余额应答
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put("cardNo", ByteBufUtil.getBytes(msgBody.readBytes(8)));
                dataMap.put("result", msgBody.readByte());
                break;
            case FRAME_TYPE_CLEAR_CARD_RES:
                // 0x45 离线卡清楚请求应答
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                //数据长度 - 设备id长度 - 序列号域 - 加密标志 - 帧类型标志 就是卡列表数据卡列表数据长度
                int cardLength = nettyMessage.getDataLength() - 24;
                int dataLen = cardLength / 20;
                List<Map<String, Object>> resMap = new ArrayList<>();
                for (int i = 0; i < dataLen; i++) {
                    Map<String, Object> res = new HashMap<>();
                    res.put("cardNo", ByteBufUtil.getBytes(msgBody.readBytes(8)));
                    res.put("mark", msgBody.readByte());
                    res.put("result", msgBody.readByte());
                    resMap.add(res);
                }
                dataMap.put("cardResult", resMap);
                break;
            case FRAME_TYPE_GET_OFFLINE_CARD_DATA_RESPONSE:
                // 0x47 离线卡数据查询应答
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                // 数据长度 - 设备id长度 - 序列号域 - 加密标志 - 帧类型标志 就是卡列表数据卡列表数据长度
                int resCardLength = nettyMessage.getDataLength() - 24;
                int resDataLen = resCardLength / 18;
                List<Map<String, Object>> resDataMap = new ArrayList<>();
                for (int i = 0; i < resDataLen; i++) {
                    Map<String, Object> res = new HashMap<>();
                    res.put("cardNo", ByteBufUtil.getBytes(msgBody.readBytes(8)));
                    res.put("result", msgBody.readByte());
                    resDataMap.add(res);
                }
                dataMap.put("cardResult", resDataMap);
                break;
            case FRAME_TYPE_LOCK_REQ:
                // 0x61 地锁数据上传
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_GUN_NO, msgBody.readByte());
                dataMap.put("lockStatus", msgBody.readByte());
                dataMap.put("parkStatus", msgBody.readByte());
                dataMap.put("powerStatus", msgBody.readByte());
                dataMap.put("alarmStatus", msgBody.readByte());
                dataMap.put("mark", msgBody.readIntLE());
                break;
            case FRAME_TYPE_REBOOT_RES:
                // 0X91远程重启
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_RESULT, msgBody.readByte());
                break;
            case FRAME_TYPE_UPDATE_RES:
                // 0x93远程更新
                dataMap.put(KEY_DEVICE_NO, ByteBufUtil.getBytes(msgBody.readBytes(DEVICE_NO_LENGTH)));
                dataMap.put(KEY_UPDATE_STATUS, msgBody.readByte());
                break;
            default:
                break;
        }
        // 避免未对完msgBody数据
        int unReadLength = msgBody.readableBytes();
        if (unReadLength > 0) {
            msgBody.skipBytes(unReadLength);
        }
        nettyMessage.setDataMap(dataMap);
        out.add(nettyMessage);
        ReferenceCountUtil.release(msgBody);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.info("...............数据解析异常 ...............");
        log.error("异常:{}", cause);
        cause.printStackTrace();
        ctx.close();
    }
}
